home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / wdiff004.lha / wdiff-0.04 / wdiff.c < prev    next >
C/C++ Source or Header  |  1992-12-23  |  32KB  |  1,268 lines

  1. /* wdiff -- front end to diff for comparing on a word per word basis.
  2.    Copyright (C) 1992 Free Software Foundation, Inc.
  3.    Francois Pinard <pinard@iro.umontreal.ca>.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. /* Exit codes values.  */
  20. #define EXIT_NO_DIFFERENCES 0    /* no differences found */
  21. #define EXIT_ANY_DIFFERENCE 1    /* some differences found */
  22. #define EXIT_OTHER_REASON 2    /* any other reason for exit */
  23.  
  24. /* It is mandatory that some `diff' program is selected for use.  The
  25.    following definition may also include the complete path.  */
  26. #ifndef DIFF_PROGRAM
  27. #define DIFF_PROGRAM "diff"
  28. #endif
  29.  
  30. /* One may also, optionnaly, define a default PAGER_PROGRAM.  This might
  31.    be done from the Makefile.  If PAGER_PROGRAM is undefined and the
  32.    PAGER environment variable is not set, none will be used.  */
  33.  
  34. /* Define the separator lines when output is inhibited.  */
  35. #define SEPARATOR_LINE \
  36.   "======================================================================"
  37.  
  38. /* Library declarations.  */
  39.  
  40. #ifdef    STDC_HEADERS
  41. #include <stdlib.h>
  42. #endif
  43.  
  44. #include <ctype.h>
  45. #include <stdio.h>
  46.  
  47. #ifdef STDC_HEADERS
  48. #include <string.h>
  49. #else /* not STDC_HEADERS */
  50. #ifdef HAVE_STRING_H
  51. #include <string.h>
  52. #else /* not HAVE_STRING_H */
  53. #include <strings.h>
  54. #endif /* not HAVE_STRING_H */
  55. #endif /* not STDC_HEADERS */
  56.  
  57. char *strstr ();
  58.  
  59. #ifdef HAVE_TPUTS
  60. #ifdef HAVE_TERMCAP_H
  61. #include <termcap.h>
  62. #else
  63. const char *tgetstr ();
  64. #endif
  65. #endif /* HAVE_TPUTS */
  66.  
  67. #include <setjmp.h>
  68. #include <signal.h>
  69. #ifndef RETSIGTYPE
  70. #define RETSIGTYPE void
  71. #endif
  72.  
  73. #if defined (HAVE_UNISTD_H)
  74. #include <unistd.h>
  75. #endif
  76.  
  77. #include "getopt.h"
  78.  
  79. char *getenv ();
  80. FILE *readpipe ();
  81. FILE *writepipe ();
  82. char *tmpnam ();
  83. void error ();
  84.  
  85. /* Declarations.  */
  86.  
  87. /* Option variables.  */
  88.  
  89. struct option const longopts[] =
  90. {
  91.   {"copyright"   , 0, NULL, 'C'},
  92.   {"no-deleted"  , 0, NULL, '1'},
  93.   {"no-inserted" , 0, NULL, '2'},
  94.   {"no-common"   , 0, NULL, '3'},
  95.   {"help"        , 0, NULL, 'h'},
  96.   {"printer"     , 0, NULL, 'p'},
  97.   {"statistics"  , 0, NULL, 's'},
  98.   {"terminal"    , 0, NULL, 't'},
  99.   {"version"     , 0, NULL, 'v'},
  100.   {"start-delete", 1, NULL, 'w'},
  101.   {"end-delete"  , 1, NULL, 'x'},
  102.   {"start-insert", 1, NULL, 'y'},
  103.   {"end-insert"  , 1, NULL, 'z'},
  104.   {NULL          , 0, NULL, 0}
  105. };
  106.  
  107. static char const version[] =
  108.   "wdiff, version 0.04";
  109. static char const copyright[] =
  110.   "Copyright (C) 1992 Free Software Foundation, Inc.";
  111.  
  112. const char *program_name;    /* name of executing program */
  113.  
  114. int inhibit_left;        /* inhibit display of left side words */
  115. int inhibit_right;        /* inhibit display of left side words */
  116. int inhibit_common;        /* inhibit display of common words */
  117. int show_statistics;        /* if printing summary statistics */
  118. int no_wrapping;        /* end/restart strings at end of lines */
  119. int autopager;            /* if calling the pager automatically */
  120. int overstrike;            /* if using printer overstrikes */
  121. int overstrike_for_less;    /* if output aimed to the "less" program */
  122. const char *user_delete_start;    /* user specified string for start of delete */
  123. const char *user_delete_end;    /* user specified string for end of delete */
  124. const char *user_insert_start;    /* user specified string for start of insert */
  125. const char *user_insert_end;    /* user specified string for end of insert */
  126.  
  127. int find_termcap;        /* initialize the termcap strings */
  128. const char *term_delete_start;    /* termcap string for start of delete */
  129. const char *term_delete_end;    /* termcap string for end of delete */
  130. const char *term_insert_start;    /* termcap string for start of insert */
  131. const char *term_insert_end;    /* termcap string for end of insert */
  132.  
  133. /* Other variables.  */
  134.  
  135. enum copy_mode
  136. {
  137.   COPY_NORMAL,            /* copy text unemphasized */
  138.   COPY_DELETED,            /* copy text underlined */
  139.   COPY_INSERTED            /* copy text bolded */
  140. }
  141. copy_mode;
  142.  
  143. jmp_buf signal_label;        /* where to jump when signal received */
  144. int jump_trigger;        /* set when some signal has been received */
  145.  
  146. /* Guarantee some value for L_tmpnam.  */
  147. #ifndef L_tmpnam
  148. #include <sys/types.h>
  149. #include "pathmax.h"
  150. #define L_tmpnam PATH_MAX
  151. #endif
  152.  
  153. typedef struct side SIDE;    /* all variables for one side */
  154. struct side
  155. {
  156.   const char *filename;        /* original input file name */
  157.   FILE *file;            /* original input file */
  158.   int position;            /* number of words read so far */
  159.   int character;        /* one character look ahead */
  160.   char temp_name[L_tmpnam];    /* temporary file name */
  161.   FILE *temp_file;        /* temporary file */
  162. };
  163. SIDE side_array[2];        /* area for holding side descriptions */
  164. SIDE *left_side = &side_array[0];
  165. SIDE *right_side = &side_array[1];
  166.  
  167. FILE *input_file;        /* stream being produced by diff */
  168. int character;            /* for reading input_file */
  169. char directive;            /* diff directive character */
  170. int argument[4];        /* four diff directive arguments */
  171.  
  172. FILE *output_file;        /* file to which we write output */
  173. const char *termcap_init_string; /* how to initialize the termcap mode */
  174. const char *termcap_end_string; /* how to complete the termcap mode */
  175.  
  176. int count_total_left;        /* count of total words in left file */
  177. int count_total_right;        /* count of total words in right file */
  178. int count_isolated_left;    /* count of deleted words in left file */
  179. int count_isolated_right;    /* count of added words in right file */
  180. int count_changed_left;        /* count of changed words in left file */
  181. int count_changed_right;    /* count of changed words in right file */
  182.  
  183. /* Signal processing.  */
  184.  
  185. /*-----------------.
  186. | Signal handler.  |
  187. `-----------------*/
  188.  
  189. RETSIGTYPE
  190. signal_handler (int number)
  191. {
  192.   jump_trigger = 1;
  193.   signal (number, signal_handler);
  194. }
  195.  
  196. /*----------------------------.
  197. | Prepare to handle signals.  |
  198. `----------------------------*/
  199.  
  200. void
  201. setup_signals (void)
  202. {
  203.   jump_trigger = 0;
  204.  
  205.   /* Intercept willingful requests for stopping.  */
  206.  
  207.   signal (SIGINT, signal_handler);
  208.   signal (SIGPIPE, signal_handler);
  209.   signal (SIGTERM, signal_handler);
  210. }
  211.  
  212.  
  213. /* Terminal initialization.  */
  214.  
  215. void
  216. initialize_strings (void)
  217. {
  218.   const char *name;        /* terminal capability name */
  219.   char term_buffer[2048];    /* terminal description */
  220.   static char *buffer;        /* buffer for capabilities */
  221.   char *filler;            /* cursor into allocated strings */
  222.   int success;            /* tgetent results */
  223.  
  224. #ifdef HAVE_TPUTS
  225.   if (find_termcap)
  226.     {
  227.       name = getenv ("TERM");
  228.       if (name == NULL)
  229.     error (1, 0, "Specify a terminal type with `setenv TERM <yourtype>'.");
  230.       success = tgetent (term_buffer, name);
  231.       if (success < 0)
  232.     error (1, 0, "Could not access the termcap data base.");
  233.       if (success == 0)
  234.     error (1, 0, "Terminal type `%s' is not defined.", name);
  235.       buffer = (char *) malloc (strlen (term_buffer));
  236.       filler = buffer;
  237.  
  238.       termcap_init_string = tgetstr ("ti", &filler);
  239.       termcap_end_string = tgetstr ("te", &filler);
  240.       term_delete_start = tgetstr ("us", &filler);
  241.       term_delete_end = tgetstr ("ue", &filler);
  242.       term_insert_start = tgetstr ("so", &filler);
  243.       term_insert_end = tgetstr ("se", &filler);
  244.     }
  245. #endif /* HAVE_TPUTS */
  246.  
  247.   /* Ensure some default strings.  */
  248.  
  249.   if (!overstrike)
  250.     {
  251.       if (!term_delete_start && !user_delete_start)
  252.     user_delete_start = "[-";
  253.       if (!term_delete_end && !user_delete_end)
  254.     user_delete_end = "-]";
  255.       if (!term_insert_start && !user_insert_start)
  256.     user_insert_start = "{+";
  257.       if (!term_insert_end && !user_insert_end)
  258.     user_insert_end = "+}";
  259.     }
  260. }
  261.  
  262.  
  263. /* Character input and output.  */
  264.  
  265. /*-----------------------------------------.
  266. | Write one character for tputs function.  |
  267. `-----------------------------------------*/
  268.  
  269. int
  270. putc_for_tputs (int chr)
  271. {
  272.   return putc (chr, output_file);
  273. }
  274.  
  275. /*---------------------------.
  276. | Indicate start of delete.  |
  277. `---------------------------*/
  278.  
  279. void
  280. start_of_delete (void)
  281. {
  282.  
  283.   /* Avoid any emphasis if it would be useless.  */
  284.  
  285.   if (inhibit_common && (inhibit_right || inhibit_left))
  286.     return;
  287.  
  288.   copy_mode = COPY_DELETED;
  289. #ifdef HAVE_TPUTS
  290.   if (term_delete_start)
  291.     tputs (term_delete_start, 0, putc_for_tputs);
  292. #endif
  293.   if (user_delete_start)
  294.     fprintf (output_file, "%s", user_delete_start);
  295. }
  296.  
  297. /*-------------------------.
  298. | Indicate end of delete.  |
  299. `-------------------------*/
  300.  
  301. void
  302. end_of_delete (void)
  303. {
  304.  
  305.   /* Avoid any emphasis if it would be useless.  */
  306.  
  307.   if (inhibit_common && (inhibit_right || inhibit_left))
  308.     return;
  309.  
  310.   if (user_delete_end)
  311.     fprintf (output_file, "%s", user_delete_end);
  312. #ifdef HAVE_TPUTS
  313.   if (term_delete_end)
  314.     tputs (term_delete_end, 0, putc_for_tputs);
  315. #endif
  316.   copy_mode = COPY_NORMAL;
  317. }
  318.  
  319. /*---------------------------.
  320. | Indicate start of insert.  |
  321. `---------------------------*/
  322.  
  323. void
  324. start_of_insert (void)
  325. {
  326.  
  327.   /* Avoid any emphasis if it would be useless.  */
  328.  
  329.   if (inhibit_common && (inhibit_right || inhibit_left))
  330.     return;
  331.  
  332.   copy_mode = COPY_INSERTED;
  333. #ifdef HAVE_TPUTS
  334.   if (term_insert_start)
  335.     tputs (term_insert_start, 0, putc_for_tputs);
  336. #endif
  337.   if (user_insert_start)
  338.     fprintf (output_file, "%s", user_insert_start);
  339. }
  340.  
  341. /*-------------------------.
  342. | Indicate end of insert.  |
  343. `-------------------------*/
  344.  
  345. void
  346. end_of_insert (void)
  347. {
  348.  
  349.   /* Avoid any emphasis if it would be useless.  */
  350.  
  351.   if (inhibit_common && (inhibit_right || inhibit_left))
  352.     return;
  353.  
  354.   if (user_insert_end)
  355.     fprintf (output_file, "%s", user_insert_end);
  356. #ifdef HAVE_TPUTS
  357.   if (term_insert_end)
  358.     tputs (term_insert_end, 0, putc_for_tputs);
  359. #endif
  360.   copy_mode = COPY_NORMAL;
  361. }
  362.  
  363. /*--------------------------------.
  364. | Skip over white space on SIDE.  |
  365. `--------------------------------*/
  366.  
  367. void
  368. skip_whitespace (SIDE *side)
  369. {
  370.   if (jump_trigger)
  371.     longjmp (signal_label, 1);
  372.  
  373.   while (isspace (side->character))
  374.     side->character = getc (side->file);
  375. }
  376.  
  377. /*------------------------------------.
  378. | Skip over non white space on SIDE.  |
  379. `------------------------------------*/
  380.  
  381. void
  382. skip_word (SIDE *side)
  383. {
  384.   if (jump_trigger)
  385.     longjmp (signal_label, 1);
  386.  
  387.   while (side->character != EOF && !isspace (side->character))
  388.     side->character = getc (side->file);
  389.   side->position++;
  390. }
  391.  
  392. /*-------------------------------------.
  393. | Copy white space from SIDE to FILE.  |
  394. `-------------------------------------*/
  395.  
  396. void
  397. copy_whitespace (SIDE *side, FILE *file)
  398. {
  399.   if (jump_trigger)
  400.     longjmp (signal_label, 1);
  401.  
  402.   while (isspace (side->character))
  403.     {
  404.  
  405.       /* While changing lines, ensure we stop any special display prior
  406.      to, and restore the special display after.  When copy_mode is
  407.      anything else than COPY_NORMAL, file is always output_file.  We
  408.      care underlining whitespace or overstriking it with itself,
  409.      because "less" understands these things as emphasis requests.  */
  410.  
  411.       switch (copy_mode)
  412.     {
  413.     case COPY_NORMAL:
  414.       putc (side->character, file);
  415.       break;
  416.  
  417.     case COPY_DELETED:
  418.       if (side->character == '\n')
  419.         {
  420.           if (no_wrapping && user_delete_end)
  421.         fprintf (output_file, "%s", user_delete_end);
  422. #ifdef HAVE_TPUTS
  423.           if (term_delete_end)
  424.         tputs (term_delete_end, 0, putc_for_tputs);
  425. #endif
  426.           putc ('\n', output_file);
  427. #ifdef HAVE_TPUTS
  428.           if (term_delete_start)
  429.         tputs (term_delete_start, 0, putc_for_tputs);
  430. #endif
  431.           if (no_wrapping && user_delete_start)
  432.         fprintf (output_file, "%s", user_delete_start);
  433.         }
  434.       else if (overstrike_for_less)
  435.         {
  436.           putc ('_', output_file);
  437.           putc ('\b', output_file);
  438.           putc (side->character, output_file);
  439.         }
  440.       else
  441.         putc (side->character, output_file);
  442.       break;
  443.  
  444.     case COPY_INSERTED:
  445.       if (side->character == '\n')
  446.         {
  447.           if (no_wrapping && user_insert_end)
  448.         fprintf (output_file, "%s", user_insert_end);
  449. #ifdef HAVE_TPUTS
  450.           if (term_insert_end)
  451.         tputs (term_insert_end, 0, putc_for_tputs);
  452. #endif
  453.           putc ('\n', output_file);
  454. #ifdef HAVE_TPUTS
  455.           if (term_insert_start)
  456.         tputs (term_insert_start, 0, putc_for_tputs);
  457. #endif
  458.           if (no_wrapping && user_insert_start)
  459.         fprintf (output_file, "%s", user_insert_start);
  460.         }
  461.       else if (overstrike_for_less)
  462.         {
  463.           putc (side->character, output_file);
  464.           putc ('\b', output_file);
  465.           putc (side->character, output_file);
  466.         }
  467.       else
  468.         putc (side->character, output_file);
  469.       break;
  470.     }
  471.  
  472.       /* Advance to next character.  */
  473.  
  474.       side->character = getc (side->file);
  475.     }
  476. }
  477.  
  478. /*-----------------------------------------.
  479. | Copy non white space from SIDE to FILE.  |
  480. `-----------------------------------------*/
  481.  
  482. void
  483. copy_word (SIDE *side, FILE *file)
  484. {
  485.   if (jump_trigger)
  486.     longjmp (signal_label, 1);
  487.  
  488.   while (side->character != EOF && !isspace (side->character))
  489.     {
  490.  
  491.       /* In printer mode, act according to copy_mode.  If copy_mode is not
  492.      COPY_NORMAL, we know that file is necessarily output_file.  */
  493.  
  494.       if (overstrike)
  495.     switch (copy_mode)
  496.       {
  497.       case COPY_NORMAL:
  498.         putc (side->character, file);
  499.         break;
  500.  
  501.       case COPY_DELETED:
  502.         putc ('_', output_file);
  503.  
  504.         /* Avoid underlining an underscore.  */
  505.  
  506.         if (side->character != '_')
  507.           {
  508.         putc ('\b', output_file);
  509.         putc (side->character, output_file);
  510.           }
  511.         break;
  512.       
  513.       case COPY_INSERTED:
  514.         putc (side->character, output_file);
  515.         putc ('\b', output_file);
  516.         putc (side->character, output_file);
  517.         break;
  518.       }
  519.       else
  520.     putc (side->character, file);
  521.  
  522.       side->character = getc (side->file);
  523.     }
  524.   side->position++;
  525. }
  526.  
  527. /*-------------------------------------------------------------------------.
  528. | For a given SIDE, turn original input file in another one, in which each |
  529. | word is on one line.                               |
  530. `-------------------------------------------------------------------------*/
  531.  
  532. void
  533. split_file_into_words (SIDE *side)
  534. {
  535.  
  536.   /* Open files.  */
  537.  
  538.   side->file = fopen (side->filename, "r");
  539.   if (side->file == NULL)
  540.     {
  541.       perror (side->filename);
  542.       exit (EXIT_OTHER_REASON);
  543.     }
  544.   side->character = getc (side->file);
  545.   side->position = 0;
  546.  
  547.   tmpnam (side->temp_name);
  548.   side->temp_file = fopen (side->temp_name, "w");
  549.   if (side->temp_file == NULL)
  550.     {
  551.       perror (side->temp_name);
  552.       exit (EXIT_OTHER_REASON);
  553.     }
  554.  
  555.   /* Complete splitting input file into words on output.  */
  556.  
  557.   while (side->character != EOF)
  558.     {
  559.       if (jump_trigger)
  560.     longjmp (signal_label, 1);
  561.  
  562.       skip_whitespace (side);
  563.       if (side->character == EOF)
  564.     break;
  565.       copy_word (side, side->temp_file);
  566.       putc ('\n', side->temp_file);
  567.     }
  568.   fclose (side->file);
  569.   fclose (side->temp_file);
  570. }
  571.  
  572. /*-------------------------------------------------------------------.
  573. | Decode one directive line from INPUT_FILE.  The format should be:  |
  574. |                                      |
  575. |      ARG0 [ , ARG1 ] LETTER ARG2 [ , ARG3 ] \n             |
  576. |                                      |
  577. | By default, ARG1 is assumed to have the value of ARG0, and ARG3 is |
  578. | assumed to have the value of ARG2.  Return 0 if any error found.   |
  579. `-------------------------------------------------------------------*/
  580.  
  581. int
  582. decode_directive_line (void)
  583. {
  584.   int value;            /* last scanned value */
  585.   int state;            /* ordinal of number being read */
  586.   int error;            /* if error seen */
  587.  
  588.   error = 0;
  589.   state = 0;
  590.   while (!error && state < 4)
  591.     {
  592.  
  593.       /* Read the next number.  ARG0 and ARG2 are mandatory.  */
  594.  
  595.       if (isdigit (character))
  596.     {
  597.       value = 0;
  598.       while (isdigit (character))
  599.         {
  600.           value = 10 * value + character - '0';
  601.           character = getc (input_file);
  602.         }
  603.     }
  604.       else if (state != 1 && state != 3)
  605.     error = 1;
  606.  
  607.       /* Assign the proper value.  */
  608.  
  609.       argument[state] = value;
  610.  
  611.       /* Skip the following character.  */
  612.  
  613.       switch (state)
  614.     {
  615.     case 0:
  616.     case 2:
  617.       if (character == ',')
  618.         character = getc (input_file);
  619.       break;
  620.       
  621.     case 1:
  622.       if (character == 'a' || character == 'd' || character == 'c')
  623.         {
  624.           directive = character;
  625.           character = getc (input_file);
  626.         }
  627.       else
  628.         error = 1;
  629.       break;
  630.  
  631.     case 3:
  632.       if (character != '\n')
  633.         error = 1;
  634.       break;
  635.     }
  636.       state++;
  637.     }
  638.  
  639.   /* Complete reading of the line and return success value.  */
  640.  
  641.   while (character != EOF && character != '\n')
  642.     character = getc (input_file);
  643.   if (character == '\n')
  644.     character = getc (input_file);
  645.  
  646.   return !error;
  647. }  
  648.  
  649. /*----------------------------------------------.
  650. | Skip SIDE until some word ORDINAL, included.  |
  651. `----------------------------------------------*/
  652.  
  653. void
  654. skip_until_ordinal (SIDE *side, int ordinal)
  655. {
  656.   while (side->position < ordinal)
  657.     {
  658.       skip_whitespace (side);
  659.       skip_word (side);
  660.     }
  661. }
  662.  
  663. /*----------------------------------------------.
  664. | Copy SIDE until some word ORDINAL, included.  |
  665. `----------------------------------------------*/
  666.  
  667. void
  668. copy_until_ordinal (SIDE *side, int ordinal)
  669. {
  670.   while (side->position < ordinal)
  671.     {
  672.       copy_whitespace (side, output_file);
  673.       copy_word (side, output_file);
  674.     }
  675. }
  676.  
  677. /*-----------------------------------------------------.
  678. | Study diff output and use it to drive reformatting.  |
  679. `-----------------------------------------------------*/
  680.  
  681. void
  682. reformat_diff_output (void)
  683. {
  684.   int resync_left;        /* word position for left resynchronisation */
  685.   int resync_right;        /* word position for rigth resynchronisation */
  686.  
  687.   /* Open input files.  */
  688.  
  689.   left_side->file = fopen (left_side->filename, "r");
  690.   if (left_side->file == NULL)
  691.     {
  692.       perror (left_side->filename);
  693.       exit (EXIT_OTHER_REASON);
  694.     }
  695.   left_side->character = getc (left_side->file);
  696.   left_side->position = 0;
  697.  
  698.   right_side->file = fopen (right_side->filename, "r");
  699.   if (right_side->file == NULL)
  700.     {
  701.       perror (right_side->filename);
  702.       exit (EXIT_OTHER_REASON);
  703.     }
  704.   right_side->character = getc (right_side->file);
  705.   right_side->position = 0;
  706.  
  707.   /* Process diff output.  */
  708.  
  709.   while (1)
  710.     {
  711.       if (jump_trigger)
  712.     longjmp (signal_label, 1);
  713.  
  714.       /* Skip any line irrelevant to this program.  */
  715.  
  716.       while (character != EOF && !isdigit (character))
  717.     {
  718.       while (character != EOF && character != '\n')
  719.         character = getc (input_file);
  720.       if (character == '\n')
  721.         character = getc (input_file);
  722.     }
  723.  
  724.       /* Get out the loop if end of file.  */
  725.  
  726.       if (character == EOF)
  727.     break;
  728.  
  729.       /* Read, decode and process one directive line.  */
  730.  
  731.       if (decode_directive_line ())
  732.     {
  733.  
  734.       /* Accumulate statistics about isolated or changed word counts.
  735.          Decide the required position on both files to resynchronize
  736.          them, just before obeying the directive.  Then, reposition
  737.          both files first, showing any needed common code along the
  738.          road.  Be careful to copy common code from the left side if
  739.          only deleted code is to be shown.  */
  740.  
  741.       switch (directive)
  742.         {
  743.         case 'a':
  744.           count_isolated_right += argument[3] - argument[2] + 1;
  745.           resync_left = argument[0];
  746.           resync_right = argument[2] - 1;
  747.           break;
  748.  
  749.         case 'd':
  750.           count_isolated_left += argument[1] - argument[0] + 1;
  751.           resync_left = argument[0] - 1;
  752.           resync_right = argument[2];
  753.           break;
  754.  
  755.         case 'c':
  756.           count_changed_left += argument[1] - argument[0] + 1;
  757.           count_changed_right += argument[3] - argument[2] + 1;
  758.           resync_left = argument[0] - 1;
  759.           resync_right = argument[2] - 1;
  760.           break;
  761.  
  762.         default:
  763.           abort ();
  764.         }
  765.  
  766.       if (!inhibit_left)
  767.         if (!inhibit_common && inhibit_right)
  768.           copy_until_ordinal (left_side, resync_left);
  769.         else
  770.           skip_until_ordinal (left_side, resync_left);
  771.  
  772.       if (!inhibit_right)
  773.         if (inhibit_common)
  774.           skip_until_ordinal (right_side, resync_right);
  775.         else
  776.           copy_until_ordinal (right_side, resync_right);
  777.  
  778.       if (!inhibit_common && inhibit_left && inhibit_right)
  779.         copy_until_ordinal (right_side, resync_right);
  780.  
  781.       /* Use separator lines to disambiguate the output.  */
  782.  
  783.       if (inhibit_left && inhibit_right)
  784.         {
  785.           if (!inhibit_common)
  786.         fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  787.         }
  788.       else if (inhibit_common)
  789.         fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  790.  
  791.       /* Show any deleted code.  */
  792.  
  793.       if ((directive == 'd' || directive == 'c') && !inhibit_left)
  794.         {
  795.           copy_whitespace (left_side, output_file);
  796.           start_of_delete ();
  797.           copy_word (left_side, output_file);
  798.           copy_until_ordinal (left_side, argument[1]);
  799.           end_of_delete ();
  800.         }
  801.  
  802.       /* Show any inserted code, or ensure skipping over it in case the
  803.          right file is used merely to show common words.  */
  804.  
  805.       if (directive == 'a' || directive == 'c')
  806.         if (inhibit_right)
  807.           {
  808.         if (!inhibit_common && inhibit_left)
  809.           skip_until_ordinal (right_side, argument[3]);
  810.           }
  811.         else
  812.           {
  813.         copy_whitespace (right_side, output_file);
  814.         start_of_insert ();
  815.         copy_word (right_side, output_file);
  816.         copy_until_ordinal (right_side, argument[3]);
  817.         end_of_insert ();
  818.           }
  819.     }
  820.     }
  821.  
  822.   /* Copy remainder of input.  Copy from left side if the user wanted to see
  823.      only the common code and deleted words.  */
  824.  
  825.   if (inhibit_common)
  826.     {
  827.       if (!inhibit_left || !inhibit_right)
  828.     fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  829.     }
  830.   else if (!inhibit_left && inhibit_right)
  831.     {
  832.       copy_until_ordinal (left_side, count_total_left);
  833.       copy_whitespace (left_side, output_file);
  834.     }
  835.   else
  836.     {
  837.       copy_until_ordinal (right_side, count_total_right);
  838.       copy_whitespace (right_side, output_file);
  839.     }
  840.  
  841.   /* Close input files.  */
  842.  
  843.   fclose (left_side->file);
  844.   fclose (right_side->file);
  845. }
  846.  
  847.  
  848. /* Launch and complete various programs.  */
  849.  
  850. /*-------------------------.
  851. | Lauch the diff program.  |
  852. `-------------------------*/
  853.  
  854. void
  855. launch_input_program (void)
  856. {
  857.   /* Launch the diff program.  */
  858.  
  859.   input_file = readpipe (DIFF_PROGRAM, left_side->temp_name,
  860.              right_side->temp_name, NULL);
  861.   if (!input_file)
  862.     {
  863.       perror (DIFF_PROGRAM);
  864.       exit (EXIT_OTHER_REASON);
  865.     }
  866.   character = getc (input_file);
  867. }
  868.  
  869. /*----------------------------.
  870. | Complete the diff program.  |
  871. `----------------------------*/
  872.  
  873. void
  874. complete_input_program (void)
  875. {
  876.   fclose (input_file);
  877.   wait (NULL);
  878. }
  879.  
  880. /*---------------------------------.
  881. | Launch the output pager if any.  |
  882. `---------------------------------*/
  883.  
  884. void
  885. launch_output_program (void)
  886. {
  887.   char *program;        /* name of the pager */
  888.  
  889.   /* Check if a output program should be called, and which one.  Avoid
  890.      all paging if only statistics are needed.  */
  891.  
  892.   if (autopager && isatty (fileno (stdout))
  893.       && !(inhibit_left && inhibit_right && inhibit_common))
  894.     {
  895.       program = getenv ("PAGER");
  896. #ifdef PAGER_PROGRAM
  897.       if (program == NULL)
  898.     program = PAGER_PROGRAM;
  899. #endif
  900.     }
  901.   else
  902.     program = NULL;
  903.  
  904.   /* Use stdout as default output.  */
  905.  
  906.   output_file = stdout;
  907.  
  908.   /* Ensure the termcap initialization string is sent to stdout right
  909.      away, never to the pager.  */
  910.  
  911. #ifdef HAVE_TPUTS
  912.   if (termcap_init_string)
  913.     {
  914.       tputs (termcap_init_string, 0, putc_for_tputs);
  915.       fflush (stdout);
  916.     }
  917. #endif
  918.  
  919.   /* If we should use a pager, launch it.  */
  920.  
  921.   if (program && *program)
  922.     {
  923.       output_file = writepipe (program, NULL);
  924.       if (!output_file)
  925.     {
  926.       perror (program);
  927.       exit (EXIT_OTHER_REASON);
  928.     }
  929.  
  930.       /* If we are paging to less, use printer mode, not display mode.  */
  931.  
  932.       if (strstr (program, "less"))
  933.     {
  934.       find_termcap = 0;
  935.       overstrike = 1;
  936.       overstrike_for_less = 1;
  937.     }
  938.     }
  939. }
  940.  
  941. /*-----------------------------.
  942. | Complete the pager program.  |
  943. `-----------------------------*/
  944.  
  945. void
  946. complete_output_program (void)
  947. {
  948.  
  949.   /* Complete any pending emphasis mode.  This would be necessary only if
  950.      some signal interrupts the normal operation of the program.  */
  951.  
  952.   switch (copy_mode)
  953.     {
  954.     case COPY_DELETED:
  955.       end_of_delete ();
  956.       break;
  957.  
  958.     case COPY_INSERTED:
  959.       end_of_insert ();
  960.       break;
  961.     
  962.     case COPY_NORMAL:
  963.       break;
  964.  
  965.     default:
  966.       abort ();
  967.     }
  968.  
  969.   /* Let the user play at will inside the pager, until s/he exits, before
  970.      proceeding any further.  */
  971.  
  972.   if (output_file && output_file != stdout)
  973.     {
  974.       fclose (output_file);
  975.       wait (NULL);
  976.     }
  977.  
  978.   /* Ensure the termcap termination string is sent to stdout, never to
  979.      the pager.  Moreover, the pager has terminated already.  */
  980.  
  981. #ifdef HAVE_TPUTS
  982.   if (termcap_end_string)
  983.     {
  984.       output_file = stdout;
  985.       tputs (termcap_end_string, 0, putc_for_tputs);
  986.     }
  987. #endif
  988. }
  989.  
  990. /*-------------------------------.
  991. | Print accumulated statistics.     |
  992. `-------------------------------*/
  993.  
  994. void
  995. print_statistics (void)
  996. {
  997.   int count_common_left;    /* words unchanged in left file */
  998.   int count_common_right;    /* words unchanged in right file */
  999.  
  1000.   count_common_left
  1001.     = count_total_left - count_isolated_left - count_changed_left;
  1002.   count_common_right
  1003.     = count_total_right - count_isolated_right - count_changed_right;
  1004.  
  1005.   printf ("%s: %d words", left_side->filename, count_total_left);
  1006.   if (count_total_left > 0)
  1007.     {
  1008.       printf ("  %d %d%% common", count_common_left,
  1009.           count_common_left * 100 / count_total_left);
  1010.       printf ("  %d %d%% deleted", count_isolated_left,
  1011.           count_isolated_left * 100 / count_total_left);
  1012.       printf ("  %d %d%% changed", count_changed_left,
  1013.           count_changed_left * 100 / count_total_left);
  1014.     }
  1015.   printf ("\n");
  1016.  
  1017.   printf ("%s: %d words", right_side->filename, count_total_right);
  1018.   if (count_total_right > 0)
  1019.     {
  1020.       printf ("  %d %d%% common", count_common_right,
  1021.           count_common_right * 100 / count_total_right);
  1022.       printf ("  %d %d%% inserted", count_isolated_right,
  1023.           count_isolated_right * 100 / count_total_right);
  1024.       printf ("  %d %d%% changed", count_changed_right,
  1025.           count_changed_right * 100 / count_total_right);
  1026.     }
  1027.   printf ("\n");
  1028. }
  1029.  
  1030.  
  1031. /* Main control.  */
  1032.  
  1033. /*-----------------------------------.
  1034. | Prints a more detailed Copyright.  |
  1035. `-----------------------------------*/
  1036.  
  1037. static void
  1038. print_copyright (void)
  1039. {
  1040.   fprintf (stderr, "\
  1041. This program is free software; you can redistribute it and/or modify\n\
  1042. it under the terms of the GNU General Public License as published by\n\
  1043. the Free Software Foundation; either version 2, or (at your option)\n\
  1044. any later version.\n\
  1045. \n\
  1046. This program is distributed in the hope that it will be useful,\n\
  1047. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  1048. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  1049. GNU General Public License for more details.\n\
  1050. \n\
  1051. You should have received a copy of the GNU General Public License\n\
  1052. along with this program; if not, write to the Free Software\n\
  1053. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
  1054. \n");
  1055. }
  1056.  
  1057. /*-----------------------------.
  1058. | Tell how to use, then exit.  |
  1059. `-----------------------------*/
  1060.  
  1061. static void
  1062. usage_and_exit (void)
  1063. {
  1064.   fprintf (stderr, "\
  1065. Usage: %s [ OPTION ... ] OLD_FILE NEW_FILE\n\
  1066. \n\
  1067.   -C, --copyright            print Copyright then exit\n\
  1068.   -1, --no-deleted           inhibit output of deleted words\n\
  1069.   -2, --no-inserted          inhibit output of inserted words\n\
  1070.   -3, --no-common            inhibit output of common words\n\
  1071.   -a, --auto-pager           automatically calls a pager\n\
  1072.   -h, --help                 print this help\n\
  1073.   -l, --less-mode            variation of printer mode for \"less\"\n\
  1074.   -n, --avoid-wraps          do not extend fields through newlines\n\
  1075.   -p, --printer              overstrike as for printers\n\
  1076.   -s, --statistics           say how many words deleted, inserted etc.\n\
  1077.   -t, --terminal             use termcap as for terminal displays\n\
  1078.   -v, --version              print program version then exit\n\
  1079.   -w, --start-delete STRING  string to mark beginning of delete region\n\
  1080.   -x, --end-delete STRING    string to mark end of delete region\n\
  1081.   -y, --start-insert STRING  string to mark beginning of insert region\n\
  1082.   -z, --end-insert STRING    string to mark end of insert region\n\
  1083. ", program_name);
  1084.  
  1085.   exit (EXIT_OTHER_REASON);
  1086. }
  1087.  
  1088. /*---------------.
  1089. | Main program.     |
  1090. `---------------*/
  1091.  
  1092. void
  1093. main (int argc, char *argv[])
  1094. {
  1095.   int option_char;        /* option character */
  1096.  
  1097.   /* Decode arguments.  */
  1098.  
  1099.   program_name = argv[0];
  1100.  
  1101.   inhibit_left = 0;
  1102.   inhibit_right = 0;
  1103.   inhibit_common = 0;
  1104.   show_statistics = 0;
  1105.   no_wrapping = 0;
  1106.   autopager = 0;
  1107.   overstrike = 0;
  1108.   overstrike_for_less = 0;
  1109.   user_delete_start = NULL;
  1110.   user_delete_end = NULL;
  1111.   user_insert_start = NULL;
  1112.   user_insert_end = NULL;
  1113.  
  1114.   find_termcap = -1;        /* undecided yet */
  1115.   term_delete_start = NULL;
  1116.   term_delete_end = NULL;
  1117.   term_insert_start = NULL;
  1118.   term_insert_end = NULL;
  1119.   copy_mode = COPY_NORMAL;
  1120.  
  1121.   count_total_left = 0;
  1122.   count_total_right = 0;
  1123.   count_isolated_left = 0;
  1124.   count_isolated_right = 0;
  1125.   count_changed_left = 0;
  1126.   count_changed_right = 0;
  1127.  
  1128.   while ((option_char
  1129.       = getopt_long (argc, argv, "123Cahdlnpstvw:x:y:z:", longopts, NULL)
  1130.       ) != EOF)
  1131.     switch (option_char)
  1132.       {
  1133.       case '1':
  1134.     inhibit_left = 1;
  1135.     break;
  1136.  
  1137.       case '2':
  1138.     inhibit_right = 1;
  1139.     break;
  1140.  
  1141.       case '3':
  1142.     inhibit_common = 1;
  1143.     break;
  1144.  
  1145.       case 'C':
  1146.     print_copyright ();
  1147.     exit (EXIT_OTHER_REASON);
  1148.  
  1149.       case 'a':
  1150.     autopager = 1;
  1151.     break;
  1152.  
  1153.       case 'l':
  1154.     if (find_termcap < 0)
  1155.       find_termcap = 0;
  1156.     overstrike = 1;
  1157.     overstrike_for_less = 1;
  1158.     break;
  1159.  
  1160.       case 'n':
  1161.     no_wrapping = 1;
  1162.     break;
  1163.  
  1164.       case 'p':
  1165.     overstrike = 1;
  1166.     break;
  1167.  
  1168.       case 's':
  1169.     show_statistics = 1;
  1170.     break;
  1171.  
  1172.       case 't':
  1173.     if (find_termcap < 0)
  1174.       {
  1175. #ifdef HAVE_TPUTS
  1176.         find_termcap = 1;
  1177. #else
  1178.         fprintf (stderr, "Cannot use -t, termcap not available.\n");
  1179.         exit (EXIT_OTHER_REASON);
  1180. #endif
  1181.       }
  1182.     break;
  1183.  
  1184.       case 'v':
  1185.     fprintf (stderr, "%s\n%s\n", version, copyright);
  1186.     exit (EXIT_OTHER_REASON);
  1187.  
  1188.       case 'w':
  1189.     user_delete_start = optarg;
  1190.     break;
  1191.  
  1192.       case 'x':
  1193.     user_delete_end = optarg;
  1194.     break;
  1195.  
  1196.       case 'y':
  1197.     user_insert_start = optarg;
  1198.     break;
  1199.  
  1200.       case 'z':
  1201.     user_insert_end = optarg;
  1202.     break;
  1203.  
  1204.       case 'h':
  1205.       default:
  1206.     usage_and_exit ();
  1207.       }
  1208.  
  1209.   if (optind + 2 != argc)
  1210.     usage_and_exit ();
  1211.  
  1212.   /* If find_termcap still undecided, consider it unset.  However, set if
  1213.      autopager is set while stdout is directed to a terminal, but this
  1214.      decision will be reversed later if the pager happens to be "less".  */
  1215.  
  1216.   if (find_termcap < 0)
  1217.     find_termcap = autopager && isatty (fileno (stdout));
  1218.  
  1219.   /* Setup file names and signals, then do it all.  */
  1220.  
  1221.   left_side->filename = argv[optind++];
  1222.   right_side->filename = argv[optind++];
  1223.   *left_side->temp_name = '\0';
  1224.   *right_side->temp_name = '\0';
  1225.  
  1226.   setup_signals ();
  1227.   input_file = NULL;
  1228.   output_file = NULL;
  1229.   termcap_init_string = NULL;
  1230.   termcap_end_string = NULL;
  1231.  
  1232.   if (!setjmp (signal_label))
  1233.     {
  1234.       split_file_into_words (left_side);
  1235.       count_total_left = left_side->position;
  1236.       split_file_into_words (right_side);
  1237.       count_total_right = right_side->position;
  1238.       launch_input_program ();
  1239.       launch_output_program ();
  1240.       initialize_strings ();
  1241.       reformat_diff_output ();
  1242.       fclose (input_file);
  1243.     }
  1244.  
  1245.   /* Clean up.  Beware that input_file and output_file might not exist,
  1246.      if a signal occurred early in the program.  */
  1247.  
  1248.   if (input_file)
  1249.     complete_input_program ();
  1250.  
  1251.   if (*left_side->temp_name)
  1252.     unlink (left_side->temp_name);
  1253.   if (*right_side->temp_name)
  1254.     unlink (right_side->temp_name);
  1255.  
  1256.   if (output_file)
  1257.     complete_output_program ();
  1258.  
  1259.   if (show_statistics)
  1260.     print_statistics ();
  1261.  
  1262.   if (count_isolated_left || count_isolated_right
  1263.       || count_changed_left || count_changed_right)
  1264.     exit (EXIT_ANY_DIFFERENCE);
  1265.   else
  1266.     exit (EXIT_NO_DIFFERENCES);
  1267. }
  1268.